Topics:

  1. marginal effects – margins

  2. robust / resistance regression – robustbase

  1. sample selection models – sampleSelection
  2. spatial analysis tmap
install.packages("margins")
install.packages("robustbase")
install.packages("sampleSelection")
install.packages("tmap")
library(robustbase)
library(sampleSelection)
library(readxl)
library(tidyverse)
library(olsrr)
library(sandwich)
library(lmtest)
library(margins)
library(tmap)
library(sf)

\[ y = \beta_1 + \beta_2X_1 + \beta_3X_1X_2 \]

\[ ME(X_1) = \beta_2 + \beta_3X_2 \] \[ ME_i(X_1) = \beta_2 + \beta_3X_{2i} \]

AME – average marginal effects

\[ \overline{ME_i(X_1)} = \frac{\sum_i \hat{\beta}_2 + \hat{\beta}_3X_{2i}}{n} \]

MEM – marginal effects at means

\[ ME_i(X_1) = \hat{\beta}_2 + \hat{\beta}_3\bar{X}_{2i} \] MER – marginal effect at representative case

\[ ME_i(X_1) = \hat{\beta}_2 + \hat{\beta}_3X_{2{i=k}} \]

a*b = a + b + a:b

ggplot(data = mtcars, aes(x = wt, y =mpg, color = factor(am), group = factor(am))) + 
  geom_point() +
  geom_smooth(method = "lm", se =F)
`geom_smooth()` using formula 'y ~ x'

example1 <- lm(formula = mpg ~ am + cyl + gear + wt + hp + am*hp + am*cyl + am*wt + am*gear,
               data = mtcars)
summary(example1)

Call:
lm(formula = mpg ~ am + cyl + gear + wt + hp + am * hp + am * 
    cyl + am * wt + am * gear, data = mtcars)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.4821 -1.4343 -0.4886  1.3113  5.3861 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)   
(Intercept) 30.3914607  8.4489862   3.597   0.0016 **
am          25.1563689 14.6860051   1.713   0.1008   
cyl         -0.6242845  0.7465580  -0.836   0.4120   
gear         0.5529184  1.8261784   0.303   0.7649   
wt          -1.8343640  0.9987027  -1.837   0.0798 . 
hp          -0.0235150  0.0212957  -1.104   0.2814   
am:hp        0.0336122  0.0346312   0.971   0.3423   
am:cyl      -0.0007546  1.3664746  -0.001   0.9996   
am:wt       -6.5358470  2.6823552  -2.437   0.0234 * 
am:gear     -2.6243563  2.9210663  -0.898   0.3787   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.369 on 22 degrees of freedom
Multiple R-squared:  0.8904,    Adjusted R-squared:  0.8455 
F-statistic: 19.85 on 9 and 22 DF,  p-value: 1.369e-08
margins(example1)
Average marginal effects
lm(formula = mpg ~ am + cyl + gear + wt + hp + am * hp + am *     cyl + am * wt + am * gear, data = mtcars)
summary(margins(example1))
pzn_rent <- read_excel("data/rent-poznan.xlsx")

set.seed(123)
pzn_rent_subset <- pzn_rent %>%
  add_count(quarter, name = "quarter_count") %>%
  filter(quarter_count >= 50) %>%
  filter(price >= 500, price <= 15000, flat_area >= 15, flat_area <= 250) %>%
  sample_n(3000)
  
pzn_rent_subset
model_pzn <- lm(formula = price ~ flat_area + flat_rooms + individual + flat_furnished + 
                  flat_for_students +  flat_balcony,
                data = pzn_rent_subset)
summary(model_pzn)

Call:
lm(formula = price ~ flat_area + flat_rooms + individual + flat_furnished + 
    flat_for_students + flat_balcony, data = pzn_rent_subset)

Residuals:
    Min      1Q  Median      3Q     Max 
-5043.1  -276.5   -53.7   223.2  9772.2 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)            553.5873    30.9729  17.873  < 2e-16 ***
flat_area               20.7297     0.8218  25.226  < 2e-16 ***
flat_rooms              85.4827    19.8450   4.308 1.70e-05 ***
individualTRUE          24.8742    25.6332   0.970  0.33193    
flat_furnishedTRUE     138.6813    25.5310   5.432 6.02e-08 ***
flat_for_studentsTRUE -169.2845    25.6333  -6.604 4.71e-11 ***
flat_balconyTRUE        64.1760    20.6216   3.112  0.00188 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 544.6 on 2993 degrees of freedom
Multiple R-squared:  0.4227,    Adjusted R-squared:  0.4215 
F-statistic: 365.2 on 6 and 2993 DF,  p-value: < 2.2e-16
plot(model_pzn)

In order to verify whether given observation is influential the following approach is taken

  1. save estimated betas from the model based on the whole dataset
  2. remove i-th observation from the data and estimate parameters on a reduced dataset

\[ dfbetas_{k,-i} = \frac{\hat{\beta}_k - \hat{\beta}_{k,-i}}{se(\hat{\beta}_{k,-i})} \]

Cooks’s distance mesure

How can we deal with influential observations?

The main difference between standard (non-robust) and robust methods is the way how loss function is calculated.

model_pzn_rob <- lmrob(formula = price ~ flat_area + flat_rooms + individual + flat_furnished + 
                                  flat_for_students +  flat_balcony,
                       data = pzn_rent_subset,
                       method = "MM")
summary(model_pzn_rob)

Call:
lmrob(formula = price ~ flat_area + flat_rooms + individual + flat_furnished + 
    flat_for_students + flat_balcony, data = pzn_rent_subset, method = "MM")
 \--> method = "MM"
Residuals:
     Min       1Q   Median       3Q      Max 
-3705.20  -217.14   -13.43   255.22  9774.74 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)            752.428     27.813  27.053  < 2e-16 ***
flat_area               12.665      1.162  10.902  < 2e-16 ***
flat_rooms             139.919     19.646   7.122 1.33e-12 ***
individualTRUE          41.007     16.476   2.489   0.0129 *  
flat_furnishedTRUE      84.644     17.560   4.820 1.51e-06 ***
flat_for_studentsTRUE  -85.358     15.842  -5.388 7.67e-08 ***
flat_balconyTRUE        74.079     14.368   5.156 2.69e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Robust residual standard error: 344.6 
Multiple R-squared:  0.4267,    Adjusted R-squared:  0.4255 
Convergence in 20 IRWLS iterations

Robustness weights: 
 53 observations c(176,198,199,208,260,284,411,540,601,629,664,684,698,723,743,761,786,851,909,943,1242,1277,1328,1350,1358,1379,1449,1460,1609,1622,1841,1889,1898,1900,2062,2080,2120,2258,2297,2301,2400,2424,2445,2463,2496,2557,2625,2663,2749,2816,2883,2909,2975)
     are outliers with |weight| = 0 ( < 3.3e-05); 
 240 weights are ~= 1. The remaining 2707 ones are summarized as
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0000354 0.8664000 0.9523000 0.8848000 0.9856000 0.9990000 
Algorithmic parameters: 
       tuning.chi                bb        tuning.psi        refine.tol           rel.tol         scale.tol         solve.tol       eps.outlier 
        1.548e+00         5.000e-01         4.685e+00         1.000e-07         1.000e-07         1.000e-10         1.000e-07         3.333e-05 
            eps.x warn.limit.reject warn.limit.meanrw 
        4.547e-10         5.000e-01         5.000e-01 
     nResample         max.it       best.r.s       k.fast.s          k.max    maxit.scale      trace.lev            mts     compute.rd fast.s.large.n 
           500             50              2              1            200            200              0           1000              0           2000 
                  psi           subsampling                   cov compute.outlier.stats 
           "bisquare"         "nonsingular"         ".vcov.avar1"                  "SM" 
seed : int(0) 
plot(x = model_pzn_rob$model$price, y = model_pzn_rob$rweights, xlab = "Price", ylab = "Weight (lmrob)")

Econometricians often use “robust” term to describe standard errors that are robust to HC or temporal correlation or clustering Statistican often use “robust” term to describe model that is robust to influential observations

Post-hoc sensitivity analysis:

Sample selection models:

  1. basic selection model: Heckman’s model
  2. more advanced econometric models: copula selection models
  3. more statistical approach: not missing at random models

Model without selection bias (subset of people who are in the labour force)

m_outcome <- lm(formula = wage ~ educ, data = Mroz87, subset  = lfp ==1)
summary(m_outcome)

Call:
lm(formula = wage ~ educ, data = Mroz87, subset = lfp == 1)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.6797 -1.6658 -0.4556  0.8794 21.1487 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -2.09237    0.84829  -2.467    0.014 *  
educ         0.49531    0.06595   7.511 3.49e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.114 on 426 degrees of freedom
Multiple R-squared:  0.1169,    Adjusted R-squared:  0.1149 
F-statistic: 56.41 on 1 and 426 DF,  p-value: 3.486e-13
data(Mroz87)
m <- selection(selection = lfp ~ educ + age + kids5 + kids618 + nwifeinc,
               outcome  = wage  ~ educ, 
               data = Mroz87, 
               method = "ml")
summary(m)
--------------------------------------------
Tobit 2 model (sample selection model)
Maximum Likelihood estimation
Newton-Raphson maximisation, 7 iterations
Return code 8: successive function values within relative tolerance limit (reltol)
Log-Likelihood: -1478.116 
753 observations (325 censored and 428 observed)
10 free parameters (df = 743)
Probit selection equation:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -1.489477   0.308468  -4.829 1.67e-06 ***
educ         0.163732   0.020126   8.136 1.72e-15 ***
age         -0.005492   0.003707  -1.482 0.138839    
kids5       -0.167565   0.052057  -3.219 0.001343 ** 
kids618      0.016332   0.020581   0.794 0.427724    
nwifeinc    -0.007270   0.002115  -3.437 0.000621 ***
Outcome equation:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -6.77655    0.96767  -7.003 5.62e-12 ***
educ         0.66434    0.07535   8.817  < 2e-16 ***
   Error terms:
      Estimate Std. Error t value Pr(>|t|)    
sigma 4.153999   0.166485   24.95   <2e-16 ***
rho   0.989398   0.004279  231.24   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
--------------------------------------------

Generate sample data

set.seed(123)
n <- 1000 
x1 <- rnorm(n = n, mean = 0, sd = 1)
x2 <- rnorm(n = n, mean = 0, sd = 1)
errors <- MASS::mvrnorm(n = n, mu = c(0, 0), 
                        Sigma = matrix(c(1, 0.5, 0.5, 2), nrow=2),
                        empirical = T)

selmodel <- data.frame(r = 1 + x1 + x2 + errors[, 1],
                       y = 1 + x1 + errors[, 2])

selmodel$sel <- selmodel$r > 0

selection(selection = sel ~ x1 + x2,outcome = y ~ x1, data = selmodel) |> 
  summary()

Spatial

df_salaries <- read_excel("data/data-salaries.xlsx", sheet = 2) %>%
  dplyr::select(id = Kod, name = Nazwa, salaries = Wartosc)

df_real <- read_excel("data/data-real-estate.xlsx", sheet = 2) %>%
  dplyr::select(id = Kod, name = Nazwa, real = Wartosc)

df_model <- df_salaries %>%
  inner_join(df_real) %>%
  filter(real > 0) %>%
  mutate(woj = substr(id,1,2),
         cities = str_detect(name, "m\\."),
         capitals = str_detect(name, "Białystok|Bydgoszcz|Gdańsk|Gorzów Wielkopolski|Katowice|Kielce|Kraków|Lublin|Łódź|Olsztyn|Opole|Poznań|Rzeszów|Szczecin|Toruń|Warszawa|Wrocław|Zielona Góra"))
Joining, by = c("id", "name")
df_model %>%
  filter(capitals)
NA
plot(log(df_model$salaries), log(df_model$real))

cor((df_model$salaries), (df_model$real))
[1] 0.4726012
model1 <- lm(formula = log(real) ~ log(salaries) + woj + capitals + cities, data = df_model)
df_model$resids <- resid(model1)
plot(model1)

summary(model1)

Call:
lm(formula = log(real) ~ log(salaries) + woj + capitals + cities, 
    data = df_model)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.6441 -0.1337 -0.0074  0.1165  0.9212 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)    0.37045    1.09041   0.340 0.734256    
log(salaries)  0.89737    0.12805   7.008 1.20e-11 ***
woj04          0.06667    0.06230   1.070 0.285261    
woj06          0.19529    0.06107   3.198 0.001508 ** 
woj08         -0.00429    0.07159  -0.060 0.952252    
woj10          0.20641    0.06053   3.410 0.000724 ***
woj12          0.40529    0.06193   6.544 2.07e-10 ***
woj14          0.22529    0.05235   4.304 2.17e-05 ***
woj16         -0.12258    0.07463  -1.643 0.101354    
woj18          0.15532    0.06110   2.542 0.011439 *  
woj20          0.07913    0.06657   1.189 0.235344    
woj22          0.37975    0.06303   6.025 4.19e-09 ***
woj24          0.04039    0.05597   0.722 0.471035    
woj26          0.08760    0.07149   1.225 0.221230    
woj28          0.09229    0.06393   1.443 0.149757    
woj30          0.14759    0.05533   2.668 0.007986 ** 
woj32          0.18194    0.06341   2.869 0.004359 ** 
capitalsTRUE   0.32291    0.06452   5.005 8.79e-07 ***
citiesTRUE     0.12809    0.03840   3.335 0.000941 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2178 on 359 degrees of freedom
Multiple R-squared:  0.4776,    Adjusted R-squared:  0.4514 
F-statistic: 18.23 on 18 and 359 DF,  p-value: < 2.2e-16
powiats <- st_read(dsn = "data/mapy/powiaty.shp")
Reading layer `powiaty' from data source `/Users/berenz/git/dydaktyka/applied-econometrics/data/mapy/powiaty.shp' using driver `ESRI Shapefile'
Simple feature collection with 380 features and 30 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 171677.6 ymin: 133223.7 xmax: 861895.7 ymax: 774923.7
CRS:           NA
woj <- st_read(dsn = "data/mapy/woj.dbf")
Reading layer `woj' from data source `/Users/berenz/git/dydaktyka/applied-econometrics/data/mapy/woj.dbf' using driver `ESRI Shapefile'
Simple feature collection with 16 features and 30 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 171677.6 ymin: 133223.7 xmax: 861882.2 ymax: 774923.7
CRS:           NA
plot(powiats$geometry)
plot(woj$geometry, add = T, lwd = 2)

powiats %>% 
  dplyr::select(id=jpt_kod_je) %>%
  left_join(df_model %>%
              mutate(id = substr(id, 1,4))) -> for_plot
Joining, by = "id"
tm_shape(for_plot) +
  tm_polygons(col = "resids", style = "jenks", midpoint = 0) +
  tm_shape(woj) + 
  tm_borders(lwd = 2)
Warning: The projection of the shape object for_plot is not known, while it seems to be projected.
Warning: Current projection of shape for_plot unknown and cannot be determined.
Warning: Current projection of shape woj unknown and cannot be determined.

Generating correlated data

library(MASS)
m <- 2
sigma <- diag(c(1,2), 2, 2)
sigma[1,2] <- sigma[2,1] <- 0.5
fake_data <- MASS::mvrnorm(n = 2000, mu=rep(0, m), Sigma = sigma, empirical = T)
cor(fake_data)
plot(fake_data)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVG9waWNzOgoKMS4gbWFyZ2luYWwgZWZmZWN0cyAtLSBtYXJnaW5zCgoyLiByb2J1c3QgLyByZXNpc3RhbmNlIHJlZ3Jlc3Npb24gLS0gcm9idXN0YmFzZSAKCi0gaW5mdWVudGlhbCBvYnMgLS0gY2hhbmdlIHNsb3BlcyAocGFyYW1ldGVycykgLyBoYXZlIHNpZ2lmaWNhbnQgZWZmZWN0IG9uIG91ciBlc3RpbWF0ZXMKLSBvdXRsaWVycyAtLSBvYnMgdGhhdCBoYXZlIGhpZ2ggLyBsYXJnZSByZXNpZHVhbCBidXQgZG8gbm90IGhhdmUgZWZmZWN0IG9uIG91ciBlc3RpbWF0ZXMKCjMuIHNhbXBsZSBzZWxlY3Rpb24gbW9kZWxzIC0tIHNhbXBsZVNlbGVjdGlvbgo0LiBzcGF0aWFsIGFuYWx5c2lzIHRtYXAKCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygibWFyZ2lucyIpCmluc3RhbGwucGFja2FnZXMoInJvYnVzdGJhc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJzYW1wbGVTZWxlY3Rpb24iKQppbnN0YWxsLnBhY2thZ2VzKCJ0bWFwIikKYGBgCgoKYGBge3J9CmxpYnJhcnkocm9idXN0YmFzZSkKbGlicmFyeShzYW1wbGVTZWxlY3Rpb24pCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShvbHNycikKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkobWFyZ2lucykKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KHNmKQpgYGAKCgokJAp5ID0gXGJldGFfMSArIFxiZXRhXzJYXzEgKyBcYmV0YV8zWF8xWF8yCiQkCgokJApNRShYXzEpID0gXGJldGFfMiArIFxiZXRhXzNYXzIKJCQKJCQKTUVfaShYXzEpID0gXGJldGFfMiArIFxiZXRhXzNYX3syaX0KJCQKCkFNRSAtLSBhdmVyYWdlIG1hcmdpbmFsIGVmZmVjdHMKCiQkClxvdmVybGluZXtNRV9pKFhfMSl9ID0gXGZyYWN7XHN1bV9pIFxoYXR7XGJldGF9XzIgKyBcaGF0e1xiZXRhfV8zWF97Mml9fXtufQokJAoKTUVNIC0tIG1hcmdpbmFsIGVmZmVjdHMgYXQgbWVhbnMKCiQkCk1FX2koWF8xKSA9IFxoYXR7XGJldGF9XzIgKyBcaGF0e1xiZXRhfV8zXGJhcntYfV97Mml9CiQkCk1FUiAtLSBtYXJnaW5hbCBlZmZlY3QgYXQgcmVwcmVzZW50YXRpdmUgY2FzZQoKCiQkCk1FX2koWF8xKSA9IFxoYXR7XGJldGF9XzIgKyBcaGF0e1xiZXRhfV8zWF97MntpPWt9fQokJApgYGB7cn0KaGVhZChtdGNhcnMpCmBgYAoKYSpiID0gYSArIGIgKyBhOmIKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgY29sb3IgPSBmYWN0b3IoYW0pLCBncm91cCA9IGZhY3RvcihhbSkpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGKQpgYGAKCmBgYHtyfQpleGFtcGxlMSA8LSBsbShmb3JtdWxhID0gbXBnIH4gYW0gKyBjeWwgKyBnZWFyICsgd3QgKyBocCArIGFtKmhwICsgYW0qY3lsICsgYW0qd3QgKyBhbSpnZWFyLAogICAgICAgICAgICAgICBkYXRhID0gbXRjYXJzKQpzdW1tYXJ5KGV4YW1wbGUxKQpgYGAKCgpgYGB7cn0KbWFyZ2lucyhleGFtcGxlMSkKc3VtbWFyeShtYXJnaW5zKGV4YW1wbGUxKSkKYGBgCgoKYGBge3J9CnB6bl9yZW50IDwtIHJlYWRfZXhjZWwoImRhdGEvcmVudC1wb3puYW4ueGxzeCIpCgpzZXQuc2VlZCgxMjMpCnB6bl9yZW50X3N1YnNldCA8LSBwem5fcmVudCAlPiUKICBhZGRfY291bnQocXVhcnRlciwgbmFtZSA9ICJxdWFydGVyX2NvdW50IikgJT4lCiAgZmlsdGVyKHF1YXJ0ZXJfY291bnQgPj0gNTApICU+JQogIGZpbHRlcihwcmljZSA+PSA1MDAsIHByaWNlIDw9IDE1MDAwLCBmbGF0X2FyZWEgPj0gMTUsIGZsYXRfYXJlYSA8PSAyNTApICU+JQogIHNhbXBsZV9uKDMwMDApCiAgCnB6bl9yZW50X3N1YnNldApgYGAKCmBgYHtyfQptb2RlbF9wem4gPC0gbG0oZm9ybXVsYSA9IHByaWNlIH4gZmxhdF9hcmVhICsgZmxhdF9yb29tcyArIGluZGl2aWR1YWwgKyBmbGF0X2Z1cm5pc2hlZCArIAogICAgICAgICAgICAgICAgICBmbGF0X2Zvcl9zdHVkZW50cyArICBmbGF0X2JhbGNvbnksCiAgICAgICAgICAgICAgICBkYXRhID0gcHpuX3JlbnRfc3Vic2V0KQpzdW1tYXJ5KG1vZGVsX3B6bikKcGxvdChtb2RlbF9wem4pCmBgYApJbiBvcmRlciB0byB2ZXJpZnkgd2hldGhlciBnaXZlbiBvYnNlcnZhdGlvbiBpcyBpbmZsdWVudGlhbCB0aGUgZm9sbG93aW5nIGFwcHJvYWNoIGlzIHRha2VuCgoxLiBzYXZlIGVzdGltYXRlZCBiZXRhcyBmcm9tIHRoZSBtb2RlbCBiYXNlZCBvbiB0aGUgd2hvbGUgZGF0YXNldAoyLiByZW1vdmUgaS10aCBvYnNlcnZhdGlvbiBmcm9tIHRoZSBkYXRhIGFuZCBlc3RpbWF0ZSBwYXJhbWV0ZXJzIG9uIGEgcmVkdWNlZCBkYXRhc2V0CgokJApkZmJldGFzX3trLC1pfSA9IFxmcmFje1xoYXR7XGJldGF9X2sgLSBcaGF0e1xiZXRhfV97aywtaX19e3NlKFxoYXR7XGJldGF9X3trLC1pfSl9CiQkCgoKYGBge3J9Cm9sc19wbG90X2RmYmV0YXMobW9kZWxfcHpuKQpgYGAKQ29va3MncyBkaXN0YW5jZSBtZXN1cmUKCmBgYHtyfQpvbHNfcGxvdF9jb29rc2RfY2hhcnQobW9kZWxfcHpuKQpgYGAKCkhvdyBjYW4gd2UgZGVhbCB3aXRoIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucz8gCgotIHJlbW92ZSB0aGVtIGJ1dCB3aGF0IGlzIHRoZSByZWFzb24gdG8gcmVtb3ZlIHNvbWUgZGF0YSBmcm9tIHlvdXIgYW5hbHlzaXM/IAotIHVzZSBtZXRob2RzIHRoYXQgYXJlIHJvYnVzdCB0byBpbmZsdWVudGlhbCBvYnNlcnZhdGlvbnMKClRoZSBtYWluIGRpZmZlcmVuY2UgYmV0d2VlbiBzdGFuZGFyZCAobm9uLXJvYnVzdCkgIGFuZCByb2J1c3QgbWV0aG9kcyBpcyB0aGUgd2F5IGhvdyBsb3NzIGZ1bmN0aW9uIGlzIGNhbGN1bGF0ZWQuIAoKLSBub24tcm9idXN0IGxpbmVhciByZWdyZXNzaW9uIC0gbm9uLXdlaWdodGVkIGxvc3MgZnVuY3Rpb24gKHN1bSBvZiBzcXVhcmVzKQotIHJvYnVzdCBsaW5lYXIgcmVncmVzc2lvbiAtLSB3ZWlnaHRlZCBsb3NzIGZ1bmN0aW9uICh3ZWlnaHRlZCBzdW0gb2Ygc3F1YXJlcykKCgpgYGB7cn0KbW9kZWxfcHpuX3JvYiA8LSBsbXJvYihmb3JtdWxhID0gcHJpY2UgfiBmbGF0X2FyZWEgKyBmbGF0X3Jvb21zICsgaW5kaXZpZHVhbCArIGZsYXRfZnVybmlzaGVkICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbGF0X2Zvcl9zdHVkZW50cyArICBmbGF0X2JhbGNvbnksCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHB6bl9yZW50X3N1YnNldCwKICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTU0iKQpzdW1tYXJ5KG1vZGVsX3B6bl9yb2IpCmBgYAoKYGBge3J9CnBsb3QoeCA9IG1vZGVsX3B6bl9yb2IkbW9kZWwkcHJpY2UsIHkgPSBtb2RlbF9wem5fcm9iJHJ3ZWlnaHRzLCB4bGFiID0gIlByaWNlIiwgeWxhYiA9ICJXZWlnaHQgKGxtcm9iKSIpCmBgYAoKYGBge3J9CmRhdGEuZnJhbWUobm9ucm9idXN0ID0gY29lZihtb2RlbF9wem4pLCByb2J1c3QgPSBjb2VmKG1vZGVsX3B6bl9yb2IpKQpgYGAKCkVjb25vbWV0cmljaWFucyBvZnRlbiB1c2UgICJyb2J1c3QiIHRlcm0gdG8gZGVzY3JpYmUgc3RhbmRhcmQgZXJyb3JzIHRoYXQgYXJlIHJvYnVzdCB0byBIQyBvciB0ZW1wb3JhbCBjb3JyZWxhdGlvbiBvciBjbHVzdGVyaW5nClN0YXRpc3RpY2FuIG9mdGVuIHVzZSAicm9idXN0IiB0ZXJtIHRvIGRlc2NyaWJlIG1vZGVsIHRoYXQgaXMgcm9idXN0IHRvIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucwoKUG9zdC1ob2Mgc2Vuc2l0aXZpdHkgYW5hbHlzaXM6CgotIHJlc2lkdWFsIGFuYWx5c2lzIC0tIG1haW5seSBmb3Igc3RhbmRhcmQgZXJyb3JzIGJ1dCBhbHNvIGZvciBhc3N1bXB0aW9ucyAoZS5nLiBsaW5lYXJpdHkpCi0gaW5mbHVlbnRpYWwgb2JzZXJ2YXRpb25zIGFuYWx5c2lzIC0tIGhvdyBzZW5zaXRpdmUgYXJlIHRoZSBlc3RpbWF0ZWQgcGFyYW1ldGVycwotIG9taXR0ZWQgdmFyaWFibGUgYmlhcwotIHZhcmlhYmxlIHNlbGVjdGlvbiBtZXRob2RzIC0tIExBU1NPLCBGT0NJCi0gc2VsZWN0aW9uIGJpYXMKCgpTYW1wbGUgc2VsZWN0aW9uIG1vZGVsczoKCjEuIGJhc2ljIHNlbGVjdGlvbiBtb2RlbDogSGVja21hbidzIG1vZGVsCjIuIG1vcmUgYWR2YW5jZWQgZWNvbm9tZXRyaWMgbW9kZWxzOiBjb3B1bGEgc2VsZWN0aW9uIG1vZGVscwozLiBtb3JlIHN0YXRpc3RpY2FsIGFwcHJvYWNoOiBub3QgbWlzc2luZyBhdCByYW5kb20gbW9kZWxzCgpNb2RlbCB3aXRob3V0IHNlbGVjdGlvbiBiaWFzIChzdWJzZXQgb2YgcGVvcGxlIHdobyBhcmUgaW4gdGhlIGxhYm91ciBmb3JjZSkKCmBgYHtyfQptX291dGNvbWUgPC0gbG0oZm9ybXVsYSA9IHdhZ2UgfiBlZHVjLCBkYXRhID0gTXJvejg3LCBzdWJzZXQgID0gbGZwID09IDEpCnN1bW1hcnkobV9vdXRjb21lKQpgYGAKCmBgYHtyfQpkYXRhKE1yb3o4NykKbSA8LSBzZWxlY3Rpb24oc2VsZWN0aW9uID0gbGZwIH4gZWR1YyArIGFnZSArIGtpZHM1ICsga2lkczYxOCArIG53aWZlaW5jLAogICAgICAgICAgICAgICBvdXRjb21lICA9IHdhZ2UgIH4gZWR1YywgCiAgICAgICAgICAgICAgIGRhdGEgPSBNcm96ODcsIAogICAgICAgICAgICAgICBtZXRob2QgPSAibWwiKQpzdW1tYXJ5KG0pCmBgYAoKCgoKR2VuZXJhdGUgc2FtcGxlIGRhdGEKCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpuIDwtIDEwMDAgCngxIDwtIHJub3JtKG4gPSBuLCBtZWFuID0gMCwgc2QgPSAxKQp4MiA8LSBybm9ybShuID0gbiwgbWVhbiA9IDAsIHNkID0gMSkKZXJyb3JzIDwtIE1BU1M6Om12cm5vcm0obiA9IG4sIG11ID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIFNpZ21hID0gbWF0cml4KGMoMSwgMC41LCAwLjUsIDIpLCBucm93PTIpLAogICAgICAgICAgICAgICAgICAgICAgICBlbXBpcmljYWwgPSBUKQoKc2VsbW9kZWwgPC0gZGF0YS5mcmFtZShyID0gMSArIHgxICsgeDIgKyBlcnJvcnNbLCAxXSwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gMSArIHgxICsgZXJyb3JzWywgMl0pCgpzZWxtb2RlbCRzZWwgPC0gc2VsbW9kZWwkciA+IDAKCnNlbGVjdGlvbihzZWxlY3Rpb24gPSBzZWwgfiB4MSArIHgyLG91dGNvbWUgPSB5IH4geDEsIGRhdGEgPSBzZWxtb2RlbCkgfD4gCiAgc3VtbWFyeSgpCmBgYAoKClNwYXRpYWwKCmBgYHtyfQpkZl9zYWxhcmllcyA8LSByZWFkX2V4Y2VsKCJkYXRhL2RhdGEtc2FsYXJpZXMueGxzeCIsIHNoZWV0ID0gMikgJT4lCiAgZHBseXI6OnNlbGVjdChpZCA9IEtvZCwgbmFtZSA9IE5hendhLCBzYWxhcmllcyA9IFdhcnRvc2MpCgpkZl9yZWFsIDwtIHJlYWRfZXhjZWwoImRhdGEvZGF0YS1yZWFsLWVzdGF0ZS54bHN4Iiwgc2hlZXQgPSAyKSAlPiUKICBkcGx5cjo6c2VsZWN0KGlkID0gS29kLCBuYW1lID0gTmF6d2EsIHJlYWwgPSBXYXJ0b3NjKQoKZGZfbW9kZWwgPC0gZGZfc2FsYXJpZXMgJT4lCiAgaW5uZXJfam9pbihkZl9yZWFsKSAlPiUKICBmaWx0ZXIocmVhbCA+IDApICU+JQogIG11dGF0ZSh3b2ogPSBzdWJzdHIoaWQsMSwyKSwKICAgICAgICAgY2l0aWVzID0gc3RyX2RldGVjdChuYW1lLCAibVxcLiIpLAogICAgICAgICBjYXBpdGFscyA9IHN0cl9kZXRlY3QobmFtZSwgIkJpYcWCeXN0b2t8QnlkZ29zemN6fEdkYcWEc2t8R29yesOzdyBXaWVsa29wb2xza2l8S2F0b3dpY2V8S2llbGNlfEtyYWvDs3d8THVibGlufMWBw7Nkxbp8T2xzenR5bnxPcG9sZXxQb3puYcWEfFJ6ZXN6w7N3fFN6Y3plY2lufFRvcnXFhHxXYXJzemF3YXxXcm9jxYJhd3xaaWVsb25hIEfDs3JhIikpCgpkZl9tb2RlbCAlPiUKICBmaWx0ZXIoY2FwaXRhbHMpCgpgYGAKCmBgYHtyfQpwbG90KGxvZyhkZl9tb2RlbCRzYWxhcmllcyksIGxvZyhkZl9tb2RlbCRyZWFsKSkKY29yKChkZl9tb2RlbCRzYWxhcmllcyksIChkZl9tb2RlbCRyZWFsKSkKYGBgCgpgYGB7cn0KbW9kZWwxIDwtIGxtKGZvcm11bGEgPSBsb2cocmVhbCkgfiBsb2coc2FsYXJpZXMpICsgd29qICsgY2FwaXRhbHMgKyBjaXRpZXMsIGRhdGEgPSBkZl9tb2RlbCkKZGZfbW9kZWwkcmVzaWRzIDwtIHJlc2lkKG1vZGVsMSkKcGxvdChtb2RlbDEpCnN1bW1hcnkobW9kZWwxKQpgYGAKCmBgYHtyfQpwb3dpYXRzIDwtIHN0X3JlYWQoZHNuID0gImRhdGEvbWFweS9wb3dpYXR5LnNocCIpCndvaiA8LSBzdF9yZWFkKGRzbiA9ICJkYXRhL21hcHkvd29qLmRiZiIpCnBsb3QocG93aWF0cyRnZW9tZXRyeSkKcGxvdCh3b2okZ2VvbWV0cnksIGFkZCA9IFQsIGx3ZCA9IDIpCmBgYAoKYGBge3J9CnBvd2lhdHMgJT4lIAogIGRwbHlyOjpzZWxlY3QoaWQ9anB0X2tvZF9qZSkgJT4lCiAgbGVmdF9qb2luKGRmX21vZGVsICU+JQogICAgICAgICAgICAgIG11dGF0ZShpZCA9IHN1YnN0cihpZCwgMSw0KSkpIC0+IGZvcl9wbG90CmBgYAoKYGBge3J9CnRtX3NoYXBlKGZvcl9wbG90KSArCiAgdG1fcG9seWdvbnMoY29sID0gInJlc2lkcyIsIHN0eWxlID0gImplbmtzIiwgbWlkcG9pbnQgPSAwKSArCiAgdG1fc2hhcGUod29qKSArIAogIHRtX2JvcmRlcnMobHdkID0gMikKYGBgCgpHZW5lcmF0aW5nIGNvcnJlbGF0ZWQgZGF0YQoKYGBge3J9CmxpYnJhcnkoTUFTUykKbSA8LSAyCnNpZ21hIDwtIGRpYWcoYygxLDIpLCAyLCAyKQpzaWdtYVsxLDJdIDwtIHNpZ21hWzIsMV0gPC0gMC41CmZha2VfZGF0YSA8LSBNQVNTOjptdnJub3JtKG4gPSAyMDAwLCBtdT1yZXAoMCwgbSksIFNpZ21hID0gc2lnbWEsIGVtcGlyaWNhbCA9IFQpCmNvcihmYWtlX2RhdGEpCnBsb3QoZmFrZV9kYXRhKQoKYGBgCgoK